package com.ccll.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ccll.constants.GlobalConstant;
import com.ccll.domain.common.BaseContextHolder;
import com.ccll.domain.dto.LoginRecordDto;
import com.ccll.domain.dto.PageDto;
import com.ccll.domain.dto.UserDto;
import com.ccll.domain.http.Result;
import com.ccll.domain.po.IpAddress;
import com.ccll.domain.po.LoginRecord;
import com.ccll.domain.po.User;
import com.ccll.mapper.LoginRecordMapper;
import com.ccll.mapper.UserMapper;
import com.ccll.service.IpService;
import com.ccll.service.LoginRecordService;
import com.ccll.service.UserService;
import com.ccll.utils.PageHelperUtil;
import com.ccll.utils.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author Seon
 * @date 2022/11/12 1:30
 */
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserService userService;

    @Autowired
    private IpService ipService;

    @Autowired
    private LoginRecordService loginRecordService;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private LoginRecordMapper loginRecordMapper;

    /**
     * 传入参数：用户名，密码，邮箱，验证码
     */
    @PostMapping("/signup")
    public Result<?> signup(@RequestBody UserDto userDto) {
        String code = userDto.getCode();
        if (!StringUtils.hasText(code)) {
            return Result.error("验证码为空，请获取验证码", 0);
        }
        // 这里用captcha: + email作为redis的key
        String redisKey = GlobalConstant.CAPTCHA + userDto.getEmail();
        String validateCode = redisCache.getCacheObject(redisKey);

        if (validateCode == null) {
            return Result.error("验证码已过期，请重新获取", 0);
        } else if (!validateCode.equals(code)) {
            return Result.error("验证码错误", 0);
        }
        boolean signup = userService.signup(userDto);
        if (signup) {
            return Result.success("注册成功");
        } else {
            return Result.error("注册失败", 0);
        }
    }

    @PostMapping("/login")
    public Result<?> login(@RequestBody User user, HttpServletRequest request) {
        if (user == null || user.getUsername() == null || user.getPassword() == null) {
            return Result.error("用户信息错误，劝君不要乱来啊", HttpStatus.UNAUTHORIZED.value());
        }
        String token = userService.login(user);
        if (token == null) {
            return Result.error("用户认证失败", HttpStatus.UNAUTHORIZED.value());
        }
        // 存储登录记录。
        IpAddress ipAddress = ipService.getIpAddress(request);
        User userByName = userService.getUserByName(user.getUsername());
        loginRecordService.saveRecordWithIp(ipAddress, userByName.getId());

        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("user", userByName);
        return Result.success(map);
    }

    @GetMapping("/logout")
    public Result<?> logout() {
        boolean logout = userService.logout();
        if (logout) {
            return Result.success(null);
        } else {
            return Result.error("登出失败", 0);
        }
    }


    /**
     * 获取用户列表（分页）
     */
    @GetMapping("/getUserPage")
    @PreAuthorize("hasAnyAuthority('system:admin')")
    public Result<?> getUserPage(PageDto pageDto) {
        Integer currentPage = pageDto.getCurrentPage();
        Integer pageSize = pageDto.getPageSize();
        Map<String, Object> pageData = PageHelperUtil.getPageData(pageDto);

        List<User> userPage = userMapper.getUserPage(pageData);
        Long total = userMapper.countUser(pageData);
        long pages = total%pageSize == 0? total/pageSize: total/pageSize + 1;

        Page<User> page = new Page<>();
        page.setCurrent(currentPage);
        page.setSize(pageSize);
        page.setPages(pages);
        page.setTotal(total);
        page.setRecords(userPage);

        return Result.success(page);
    }

    /**
     * 查询单个用户信息
     *
     * @param userId
     * @return
     */
    @GetMapping("/getUserInformation")
    public Result<?> getUserInformation(@RequestParam("id") Long userId) {
        User user = userService.getById(userId);
        if (user != null) {
            return Result.success(user);
        } else {
            return Result.error("用户不存在", 0);
        }
    }

    /**
     * 删除用户
     *
     * @param ids
     * @return
     */
    @DeleteMapping("/deleteUser")
    @PreAuthorize("hasAnyAuthority('system:admin')")
    public Result<?> deleteUser(@RequestParam("ids") List<Long> ids) {
        boolean remove = userService.removeByIds(ids);

        if (remove) {
            return Result.success("删除成功");
        } else {
            return Result.error("删除失败", 0);
        }
    }

    /**
     * 更新用户信息
     *
     * @param user
     * @return
     */
    @PutMapping("/modifyInformation")
    public Result<?> modifyInformation(@RequestBody User user) {
        if (user == null) {
            return Result.error("用户数据不能为空", 0);
        }
        Long userId = BaseContextHolder.getCurrentId();
        user.setId(userId);
        boolean update = userService.updateById(user);
        if (update) {
            User newUser = userService.getById(userId);
            return Result.success(newUser);
        } else {
            return Result.error("更新失败,请检查数据", 0);
        }
    }

    /**
     * 更改密码
     *
     * @param map 里面有两个键 {originalPassword, newPassword}代表原密码和新更改的密码。
     * @return
     */
    @PutMapping("/modifyPassword")
    public Result<?> modifyPassword(@RequestBody Map<String, Object> map) {
        @NotNull String originalPassword = (String) map.get("originalPassword");
        @NotNull String newPassword = (String) map.get("newPassword");
        Long userId = BaseContextHolder.getCurrentId();
        User user = userService.getById(userId);

        boolean matches = passwordEncoder.matches(originalPassword, user.getPassword());
        if (matches) {
            String encode = passwordEncoder.encode(newPassword);
            user.setPassword(encode);
            userService.updateById(user);
            return Result.success("密码更改成功");
        } else {
            return Result.error("原密码错误", 0);
        }
    }

    /**
     * 查询用户登录记录（分页）
     *
     * @param pageDto
     * @return
     */
    @GetMapping("/getLoginRecordPage")
    @PreAuthorize("hasAnyAuthority('system:admin')")
    public Result<?> getLoginRecordPage(PageDto pageDto) {
        Page<LoginRecord> page = new Page<>();
        Page<LoginRecordDto> loginPage = new Page<>();

        LambdaQueryWrapper<LoginRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(LoginRecord::getCreateTime);
        page.setCurrent(pageDto.getCurrentPage());
        page.setSize(pageDto.getPageSize());
        loginRecordService.page(page, wrapper);

        BeanUtils.copyProperties(page, loginPage, "records");

        List<LoginRecord> records = page.getRecords();
        List<LoginRecordDto> collect = records.stream().map(item -> {
            LoginRecordDto dto = new LoginRecordDto();
            BeanUtils.copyProperties(item, dto);
            String username = loginRecordMapper.getLoginUsername(dto.getUserId());
            dto.setUsername(username);
            return dto;
        }).collect(Collectors.toList());
        loginPage.setRecords(collect);
        return Result.success(loginPage);
    }

    /**
     * 权限测试类
     */
    @GetMapping("/test")
    @PreAuthorize("hasAnyAuthority('system:admin')")
    public Result<?> test() {
        return new Result<>(200, "南风知我意", "吹梦到西州");
    }
}

